#include <cstdint>
#include <iostream>
#include <ctime>
#include <string>
#include <map>
#include <algorithm>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/typeof/typeof.hpp>

class FuncTimer
{
public:
	int operator()( const char *func_name, boost::function< int(void) > func )
	{
		struct timespec begin, end;
		clock_gettime( CLOCK_MONOTONIC, &begin );
		int rc = func();
		clock_gettime( CLOCK_MONOTONIC, &end );

		uint64_t used = (uint64_t)( end.tv_sec - begin.tv_sec ) * 1000ULL * 1000ULL * 1000ULL
			+ (unsigned long)end.tv_nsec - (unsigned long)begin.tv_nsec;

		BOOST_AUTO( it, m_time_map.find( func_name ) );
		if( it == m_time_map.end() )
			m_time_map.insert( std::make_pair( func_name, used ) );
		else
			it->second += used;
		return rc;
	}

	int Print()
	{
		int num = 0;

		std::for_each( m_time_map.begin(), m_time_map.end(), [&num]( std::pair< const char *, uint64_t > t ){
				num++; std::cout << t.first << " :" << t.second  << " (nanoseconds)"  << std::endl; } );

		/*
		for( BOOST_AUTO( it, m_time_map.begin() ); it != m_time_map.end(); ++it ) {
			num ++;
			std::cout << it->first << " :" << it->second  << " (nanoseconds)"  << std::endl;
		}
		*/

		return num;
	}

private:
	std::map< const char *, uint64_t> m_time_map;

};


int f1( void )
{
	std::cout << "calling f1()" << std::endl;
	return 0;
}

int f2( int x )
{
	std::cout << "calling f2( " << x << " )" << std::endl;
	return x << 1;
}

int main( int argc, char *argv[] )
{
	FuncTimer timer;

	timer( "f1", f1 );
	std::cout << "f2 return value: " <<  timer( "f2", boost::bind( f2, 3 ) ) << std::endl;

	std::string str = "str";
	timer( "reserve", [&str](){ str.reserve( 1000 );return 0;} );

	timer( "&FuncTimer::Print", boost::bind( &FuncTimer::Print, boost::ref( timer ) ) );
	std::cout << "------------------------------------ " << std::endl;

#define TIMED_FUNC( func, ... ) timer( #func, boost::bind( func, ##__VA_ARGS__ ))

	TIMED_FUNC( f1 );
	std::cout << "f2 return value: " <<  TIMED_FUNC( f2, 3 ) << std::endl;
	TIMED_FUNC( &FuncTimer::Print, boost::ref( timer ) );
	std::cout << "------------------------------------ " << std::endl;

	timer.Print();
}

